﻿using System;
using System.Collections.Generic;

namespace g4
{
    /// <summary>
    /// 网格迭代平滑器：通过多轮迭代调整指定顶点位置，实现网格平滑效果
    /// 支持多种平滑算法（均匀权重、余切权重、均值权重），并可自定义投影函数控制顶点位置
    /// </summary>
    public class MeshIterativeSmooth
    {
        /// <summary>
        /// 待平滑的目标网格
        /// </summary>
        public DMesh3 Mesh;

        /// <summary>
        /// 需要进行平滑处理的顶点ID数组
        /// </summary>
        public int[] Vertices;

        /// <summary>
        /// 平滑系数（控制每次迭代的移动幅度，范围0~1，值越大平滑效果越强）
        /// </summary>
        public double Alpha = 0.25f;

        /// <summary>
        /// 平滑迭代次数（次数越多平滑效果越明显，但计算成本越高）
        /// </summary>
        public int Rounds = 10;

        /// <summary>
        /// 平滑算法类型枚举
        /// </summary>
        public enum SmoothTypes
        {
            Uniform,   // 均匀权重平滑（每个邻居权重相同）
            Cotan,     // 余切权重平滑（基于边的余切值计算权重，保留更多几何特征）
            MeanValue  // 均值权重平滑（基于顶点间角度计算权重，适合保形平滑）
        };

        /// <summary>
        /// 选择使用的平滑算法
        /// </summary>
        public SmoothTypes SmoothType = SmoothTypes.Uniform;

        /// <summary>
        /// 自定义投影函数：用于将平滑后的顶点位置重新投影到特定表面（如约束到原始网格或其他曲面）
        /// 输入：平滑后的位置、顶点法向量、顶点ID；输出：投影后的位置
        /// </summary>
        public Func<Vector3d, Vector3f, int, Vector3d> ProjectF;

        /// <summary>
        /// 存储每轮迭代后顶点的平滑位置（临时缓冲区）
        /// </summary>
        Vector3d[] SmoothedPostions;


        /// <summary>
        /// 构造函数：初始化迭代平滑器
        /// </summary>
        /// <param name="mesh">目标网格</param>
        /// <param name="vertices">需要平滑的顶点ID数组</param>
        /// <param name="bOwnVertices">是否拥有顶点数组的所有权（true则直接使用，false则复制数组）</param>
        public MeshIterativeSmooth(DMesh3 mesh, int[] vertices, bool bOwnVertices = false)
        {
            Mesh = mesh;
            // 根据参数决定是否复制顶点数组（避免外部修改影响内部处理）
            Vertices = (bOwnVertices) ? vertices : (int[])vertices.Clone();

            // 初始化平滑位置缓冲区（与顶点数组长度一致）
            SmoothedPostions = new Vector3d[Vertices.Length];

            // 初始投影函数为null（不进行额外投影）
            ProjectF = null;
        }


        /// <summary>
        /// 验证操作的合法性（当前无特殊验证逻辑）
        /// </summary>
        /// <returns>验证状态（始终返回Ok）</returns>
        public virtual ValidationStatus Validate()
        {
            return ValidationStatus.Ok;
        }


        /// <summary>
        /// 执行迭代平滑操作
        /// </summary>
        /// <returns>操作是否成功</returns>
        public virtual bool Smooth()
        {
            int vertexCount = Vertices.Length;
            if (vertexCount == 0)
                return true; // 无顶点需要平滑，直接返回成功

            // 限制平滑系数和迭代次数在有效范围内
            double smoothFactor = MathUtil.Clamp(Alpha, 0, 1);
            int iterations = MathUtil.Clamp(Rounds, 0, 10000);
            if (iterations == 0)
                return true; // 迭代次数为0，无需处理

            // 根据选择的平滑类型，绑定对应的平滑函数
            Func<DMesh3, int, double, Vector3d> smoothFunc = MeshUtil.UniformSmooth;
            if (SmoothType == SmoothTypes.MeanValue)
                smoothFunc = MeshUtil.MeanValueSmooth;
            else if (SmoothType == SmoothTypes.Cotan)
                smoothFunc = MeshUtil.CotanSmooth;

            // 定义单顶点平滑操作（并行执行）
            Action<int> smoothVertex = (i) => {
                int vid = Vertices[i];
                // 计算顶点的平滑位置：当前位置与邻居加权平均的混合
                SmoothedPostions[i] = smoothFunc(Mesh, vid, smoothFactor);
            };

            // 定义投影操作（若启用自定义投影）
            Action<int> projectVertex = (i) => {
                Vector3d smoothedPos = SmoothedPostions[i];
                // 调用投影函数调整位置（法向量参数暂用Y轴，可根据实际需求修改）
                SmoothedPostions[i] = ProjectF(smoothedPos, Vector3f.AxisY, Vertices[i]);
            };

            // 创建顶点索引枚举器（用于并行迭代）
            IndexRangeEnumerator indices = new IndexRangeEnumerator(0, vertexCount);

            // 执行多轮平滑迭代
            for (int round = 0; round < iterations; ++round)
            {
                // 并行计算所有顶点的平滑位置
                gParallel.ForEach<int>(indices, smoothVertex);

                // 若有自定义投影函数，应用投影调整
                if (ProjectF != null)
                    gParallel.ForEach<int>(indices, projectVertex);

                // 将平滑后的位置写回网格（"烘焙"当前轮次的结果）
                for (int i = 0; i < vertexCount; ++i)
                {
                    Mesh.SetVertex(Vertices[i], SmoothedPostions[i]);
                }
            }

            return true;
        }
    }
}